package com.cys.freemarker;

import com.org.cys.pdm.PDM;
import com.org.cys.pdm.PDMColumn;
import com.org.cys.pdm.PDMTable;
import com.org.cys.pdm.Parser;
import freemarker.template.Configuration;
import freemarker.template.Template;

import java.io.*;
import java.util.*;


/**
 * 生成代码生成器主类
 *
 * @author caiyushen 2017-09-09
 */
public class FreeMarker {

    //模块-包名
    private static String packageName = "choice";

    //pdm文件路径
    private static String pdmFilePath = "D:" + File.separator + "freemarkerCode" + File.separator + "design.PDM";

    //放置模板文件路径前缀
    private static String templateFilePath = "D:" + File.separator + "ideaWorkSpace" + File.separator + "freemarker-pdm" + File.separator + "resource" + File.separator;

    //文件输出路径
    private static String outFilePath = "D:" + File.separator + "freemarkerCode" + File.separator;

    //作者
    protected static String author = "caiyushen";

    //测试方法
    public static void main(String[] args) throws Exception {
        //指向指挥调度模板路径
        templateFilePath += File.separator + "exam";
        createDefaultFile(pdmFilePath, packageName, templateFilePath, outFilePath);
    }

    /**
     * 生成文件
     *
     * @param pdmFilePath      pdm文件路径
     * @param packageName      包名
     * @param templateFilePath ftl模板文件路径
     * @param templateFileName ftl模板文件名称 IEntity.ftl
     * @param outFilePath      生成文件路径及文件名
     * @author caiyushen
     * @createDate 2018-02-23
     */
    public static void createFile(String pdmFilePath, String packageName, String templateFilePath, String templateFileName, String outFilePath) throws Exception {
        Parser parser = new Parser();
        try {
            //读取pdm文件
            PDM pdm = parser.pdmParser(pdmFilePath);
            //获取表的数据
            ArrayList<PDMTable> pdmTableList = pdm.getTables();
            int pdmTableListSize = pdmTableList.size();
            //遍历所有表数据
            for (int i = 0; i < pdmTableListSize; i++) {
                //获取一个表数据
                PDMTable pdmTable = pdmTableList.get(i);
                getCodeFile(pdmTable, packageName, outFilePath, templateFilePath, templateFileName);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 生成默认文件
     *
     * @param pdmFilePath      pdm文件路径
     * @param packageName      包名
     * @param templateFilePath ftl模板文件路径
     * @param outFilePath      生成文件路径
     * @author caiyushen
     * @createDate 2018-02-23
     */
    public static void createDefaultFile(String pdmFilePath, String packageName, String templateFilePath, String outFilePath) throws Exception {
        Parser parser = new Parser();
        //读取pdm文件
        PDM pdm = parser.pdmParser(pdmFilePath);
        //获取表的数据
        ArrayList<PDMTable> pdmTableList = pdm.getTables();
        int pdmTableListSize = pdmTableList.size();
        //文件夹名称
        String IEntityPath = outFilePath + "IEntity";
        judeDirExists(IEntityPath);

        String EntityPath = outFilePath + "Entity";
        judeDirExists(EntityPath);

        String DaoPath = outFilePath + "Dao";
        judeDirExists(DaoPath);

        String ServicePath = outFilePath + "Service";
        judeDirExists(ServicePath);

        String ServiceImplPath = outFilePath + "ServiceImpl";
        judeDirExists(ServiceImplPath);

        String MapperPath = outFilePath + "Mapper";
        judeDirExists(MapperPath);

        String ServiceTestPath = outFilePath + "ServiceTest";
        judeDirExists(ServiceTestPath);


        //遍历所有表数据
        for (int i = 0; i < pdmTableListSize; i++) {
            //获取一个表数据
            PDMTable pdmTable = pdmTableList.get(i);
            String entityName = switchEntityName(pdmTable);// 获取实体类名

            //实体类接口
            getCodeFile(pdmTable, packageName, IEntityPath + File.separator + "I" + entityName + ".java", templateFilePath,
                    "IEntityMarker.ftl");
            //实体类
            getCodeFile(pdmTable, packageName, EntityPath + File.separator + entityName + ".java", templateFilePath,
                    "EntityMarker.ftl");
            //Dao层接口
            getCodeFile(pdmTable, packageName, DaoPath + File.separator + entityName + "Dao.java", templateFilePath,
                    "IDaoMarker.ftl");
            //Service层接口
            getCodeFile(pdmTable, packageName, ServicePath + File.separator + entityName + "Service.java", templateFilePath,
                    "ServiceMarker.ftl");
            //Service层实现类
            getCodeFile(pdmTable, packageName, ServiceImplPath + File.separator + entityName + "ServiceImpl.java", templateFilePath,
                    "ServiceImplMarker.ftl");
            //xmlMapper文件
            getCodeFile(pdmTable, packageName, MapperPath + File.separator + switchEntityNameXML(pdmTable) + "_Mapper.xml", templateFilePath,
                    "MapperXMLMarker.ftl");
            //模块单元测试
            getCodeFile(pdmTable, packageName, ServiceTestPath + File.separator + entityName + "ServiceImplTest.java", templateFilePath,
                    "TestServiceMarker.ftl");

        }
    }


    /**
     * 通过文件名加载模板
     *
     * @param ftlName 模板文件名
     * @param ftlPath 模板路径
     */
    public static Template getTemplate(String ftlName, String ftlPath) throws Exception {
        try {
            Configuration cfg = new Configuration();    //通过Freemaker的Configuration读取相应的ftl
            cfg.setEncoding(Locale.CHINA, "utf-8");
            cfg.setDirectoryForTemplateLoading(new File(ftlPath));    //设定去哪里读取相应的ftl模板文件
            Template temp = cfg.getTemplate(ftlName);    //在模板文件目录中找到名称为name的文件
            return temp;
        } catch (IOException e) {
            e.printStackTrace();
        }

        return null;
    }


    /**
     * 将table的字段数据转化变量数据
     *
     * @param pdmTable 单表数据
     * @author caiyushen
     */
    public static List<Map<String, Object>> switchTable(PDMTable pdmTable) {
        //获取该表所有字段数据
        ArrayList<PDMColumn> columnsList = pdmTable.getColumns();
        List<Map<String, Object>> dataList = new ArrayList();
        for (int i = 0; i < columnsList.size(); i++) {
            //获取一个字段数据
            PDMColumn column = columnsList.get(i);
            String newGetSetCode = switchColumnGetSet(column);
            //将字段名转化为变量名
            String newColumn = switchColumn(column);
            Map<String, Object> resultMap = new HashMap<>();
            resultMap.put("newGetSetCode", newGetSetCode); //新的getset变量
            resultMap.put("newColumn", newColumn);  //新变量
            resultMap.put("comment", column.getComment()); //字段注释
            resultMap.put("dataType", switchObjectByOracle(column)); //字段类型
            resultMap.put("columnName", column.getName()); //数据库字段名字
            dataList.add(resultMap);
        }
        return dataList;
    }

    /**
     * 生成模板文件
     *
     * @param file        新增的文件名以及路径
     * @param template    指定的模板文件
     * @param pdmTable    数据库表实体类
     * @param packageName 包名-模块名
     * @param entityName  实体类名称
     */
    public static void getFile(File file, Template template, PDMTable pdmTable, String packageName, String entityName) {
        try {

            //获取实体类名的变量（小写）
            String entityNameSmall = switchEntityNameSmall(pdmTable);

            //获取XML名: BASIC_CASE
            String entityNameXML = switchEntityNameXML(pdmTable);
            List<Map<String, Object>> dataList = switchTable(pdmTable);

            Map<String, Object> context = new HashMap<String, Object>();
            context.put("entityName", entityName);
            context.put("entityNameSmall", entityNameSmall);
            context.put("entityNameXML", entityNameXML);
            context.put("entityComment", pdmTable.getName());
            context.put("tableName", pdmTable.getCode());//数据库表名
            context.put("dataList", dataList);
            context.put("package", packageName);//包名-模块名
            context.put("author", author);
            Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), "utf-8"));
            template.process(context, out);
            out.flush();
            out.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 生成代码文件
     *
     * @param pdmTable         数据库表实体类
     * @param packageName      包名-模块名
     * @param outFilePath      输出文件路径以及名称
     * @param templateFilePath 模板路径
     * @param templateFileName 模板名称 entity.ftl
     */
    public static void getCodeFile(PDMTable pdmTable, String packageName, String outFilePath, String templateFilePath, String templateFileName) {
        try {
            //获取实体类名
            String entityName = switchEntityName(pdmTable);
            File file = new File(outFilePath);
            //指定模板
            Template template = getTemplate(templateFileName, templateFilePath);
            //生成文件
            getFile(file, template, pdmTable, packageName, entityName);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }



    /**
     * 生成实体类接口
     *
     * @param pdmTable    数据库表实体类
     * @param packageName 包名-模块名
     */
    public static void getDefaultEntityInterface(PDMTable pdmTable, String packageName, String outFilePath, String templateFilePath) {
        String entityName = switchEntityName(pdmTable);//获取实体类名
        getCodeFile(pdmTable, packageName, outFilePath + "I" + entityName + ".java", templateFilePath, "IEntityMarker.ftl");
    }

    /**
     * 生成实体类
     *
     * @param pdmTable    数据库表实体类
     * @param packageName 包名-模块名
     */
    public static void getDefaultEntity(PDMTable pdmTable, String packageName, String outFilePath, String templateFilePath) {
        String entityName = switchEntityName(pdmTable);//获取实体类名
        getCodeFile(pdmTable, packageName, outFilePath + entityName + ".java", templateFilePath, "EntityMarker.ftl");
    }

    /**
     * 生成Dao接口
     *
     * @param pdmTable    数据库表实体类
     * @param packageName 包名-模块名
     */
    public static void getDefaultIDao(PDMTable pdmTable, String packageName, String outFilePath, String templateFilePath) {
        String entityName = switchEntityName(pdmTable);//获取实体类名
        getCodeFile(pdmTable, packageName, outFilePath + entityName + "Dao.java", templateFilePath, "IDaoMarker.ftl");
    }

    /**
     * 生成Dao实现类
     *
     * @param pdmTable    数据库表实体类
     * @param packageName 包名-模块名
     */
    public static void getDefaultDao(PDMTable pdmTable, String packageName, String outFilePath, String templateFilePath) {
        //获取实体类名
        String entityName = switchEntityName(pdmTable);
        getCodeFile(pdmTable, packageName, outFilePath + entityName + "DaoImpl.java", templateFilePath, "DaoImplMarker.ftl");
    }

    /**
     * 生成XML文件
     *
     * @param pdmTable    数据库表实体类
     * @param packageName 包名-模块名
     */
    public static void getDefaultMapperXml(PDMTable pdmTable, String packageName, String outFilePath, String templateFilePath) {
        getCodeFile(pdmTable, packageName, outFilePath + switchEntityNameXML(pdmTable) + "_Mapper.xml", templateFilePath, "MapperXMLMarker.ftl");
    }

    /**
     * 生成Service接口
     *
     * @param pdmTable    数据库表实体类
     * @param packageName 包名-模块名
     */
    public static void getDefaultService(PDMTable pdmTable, String packageName, String outFilePath, String templateFilePath) {
        //获取实体类名
        String entityName = switchEntityName(pdmTable);
        getCodeFile(pdmTable, packageName, outFilePath + entityName + "Service.java", templateFilePath, "ServiceMarker.ftl");
    }


    /**
     * 生成Service实体类
     *
     * @param pdmTable    数据库表实体类
     * @param packageName 包名-模块名
     */
    public static void getDefaultServiceImpl(PDMTable pdmTable, String packageName, String outFilePath, String templateFilePath) {
        //获取实体类名
        String entityName = switchEntityName(pdmTable);
        getCodeFile(pdmTable, packageName, outFilePath + entityName + "ServiceImpl.java", templateFilePath, "ServiceImplMarker.ftl");
    }

    /**
     * 生成Service单元测试
     *
     * @param pdmTable    数据库表实体类
     * @param packageName 包名-模块名
     */
    public static void getDefaultTestService(PDMTable pdmTable, String packageName, String outFilePath, String templateFilePath) {
        //获取实体类名
        String entityName = switchEntityName(pdmTable);
        getCodeFile(pdmTable, packageName, outFilePath + entityName + "ServiceImplTest.java", templateFilePath, "TestServiceMarker.ftl");
    }


    /**
     * 根据数据表字段类型转换为对应封装的JAVA类型(适用于Oracle)
     */
    public static String switchObjectByOracle(PDMColumn column) {
        String resultObj = "";
        String dataType = column.getDataType().substring(0, 1);
        String dataType2 = column.getDataType().substring(0, 2);
        if (dataType.equals("N") || dataType.equals("n")) {//NUMBER number
            resultObj = "Long";
            return resultObj;
        }

        if (dataType.equals("I") || dataType.equals("i")) {//INT
            resultObj = "Integer";
            return resultObj;
        }

        if (dataType.equals("C") || dataType.equals("V") || dataType.equals("v")) {///CLOB VARCHAR varchar
            resultObj = "String";
            return resultObj;
        }

        if (dataType.equals("F")) {//FLOAT
            resultObj = "Float";
            return resultObj;
        }

        if (dataType2.equals("DO") || dataType2.equals("do")) {//Dobule double
            resultObj = "Double";
            return resultObj;
        }

        if (dataType2.equals("DA") || dataType2.equals("da")) {//DATE date
            resultObj = "Date";
            return resultObj;
        }

        System.out.println("name: " + column.getName() + " dataType:" + resultObj);
        return resultObj;
    }

    /**
     * 将数据表名转化为实体类名
     * 除了T以外作为实体类名
     */
    public static String switchEntityName(PDMTable pdmTable) {
        String tableName = pdmTable.getCode();
        String[] array = tableName.split("_");
        String newCode = "";
        if (array != null && array.length > 1) {
            for (int j = 1; j < array.length; j++) {
                tableName = array[j];
                char[] c = tableName.toCharArray();
                String replaceCode = "";
                for (int i = 0; i < c.length; i++) {
                    if (i > 0) {
                        replaceCode += c[i];
                    }
                }
                newCode += c[0] + exChange(replaceCode);
                System.out.println("新变量名：" + newCode);
            }
        }
        return newCode;
    }

    /**
     * 将数据表名转化为变量名(小写)
     * 除了T以外作为实体类名
     */
    public static String switchEntityNameSmall(PDMTable pdmTable) {
        String tableName = pdmTable.getCode();
        String[] array = tableName.split("_");
        String newCode = "";
        if (array != null && array.length > 1) {
            for (int j = 1; j < array.length; j++) {
                tableName = array[j];
                char[] c = tableName.toCharArray();
                if (j >= 2) {
                    String replaceCode = "";
                    for (int i = 0; i < c.length; i++) {
                        if (i > 0) {
                            replaceCode += c[i];
                        }
                    }
                    newCode += c[0] + exChange(replaceCode);
                } else {
                    String replaceCode = "";
                    for (int i = 0; i < c.length; i++) {
                        replaceCode += c[i];
                    }
                    newCode += exChange(replaceCode);
                }
                System.out.println("新实体类名：" + newCode);
            }
        }
        return newCode;
    }

    /**
     * 将数据表名转化为XML文件名
     */
    public static String switchEntityNameXML(PDMTable pdmTable) {
        String tableName = pdmTable.getCode();
        String[] array = tableName.split("_");
        String newCode = "";
        if (array != null && array.length > 1) {
            for (int j = 1; j < array.length; j++) {//去掉T
                tableName = array[j];
                char[] c = tableName.toCharArray();
                String replaceCode = "";
                for (int i = 0; i < c.length; i++) {
                    if (i > 0) {
                        replaceCode += c[i];
                    }
                }
                if (j == array.length - 1) {
                    newCode += c[0] + exChange(replaceCode);
                } else {
                    newCode += c[0] + exChange(replaceCode) + "_";
                }

            }
        }
        System.out.println("新实体类名XML：" + newCode);
        return newCode;
    }


    /**
     * 把数据表字段转化为变量名
     */
    public static String switchColumn(PDMColumn column) {
        String newCode = "";
        String[] array = column.getCode().split("_");
        for (int k = 0; k < array.length; k++) {
            String codeChar = array[k];
            if (k > 0) {
                char[] c = codeChar.toCharArray();
                String replaceCode = "";
                for (int l = 0; l < c.length; l++) {
                    if (l > 0) {
                        replaceCode += c[l];
                    }
                }
                newCode += c[0] + exChange(replaceCode);
            } else {
                newCode += exChange(codeChar);
            }
        }
        if (newCode.equals("IId") || newCode.equals("iId")) {
            System.out.println("转换后的新变量：id");
            newCode = "id";
        } else {
            System.out.println("转换后的新变量：" + newCode);
        }
        return newCode;
    }


    /**
     * 把数据表字段转化为getSet方法名字
     */
    public static String switchColumnGetSet(PDMColumn column) {
        String newCode = "";
        String[] array = column.getCode().split("_");
        for (int i = 0; i < array.length; i++) {
            String codeChar = array[i];
            char[] c = codeChar.toCharArray();
            String replaceCode = "";
            for (int j = 0; j < c.length; j++) {
                if (j > 0) {
                    replaceCode += c[j];
                }
            }
            newCode += c[0] + exChange(replaceCode);
        }

        if (newCode.equals("Iid") || newCode.equals("IId")) {
            System.out.println("转换后的get、set新变量：Id");
            newCode = "Id";
        } else {
            System.out.println("转换后的get、set新变量：" + newCode);
        }
        return newCode;
    }


    /**
     * 把一个字符串中的大写转为小写,小写转大写
     */
    public static String exChange(String str) {
        StringBuffer sb = new StringBuffer();
        if (str != null) {
            for (int i = 0; i < str.length(); i++) {
                char c = str.charAt(i);
                if (Character.isUpperCase(c)) {
                    sb.append(Character.toLowerCase(c));
                } else if (Character.isLowerCase(c)) {
                    sb.append(Character.toUpperCase(c));
                }
            }
        }
        return sb.toString();
    }

    /**
     * 判断文件夹是否存在,不存在则自己建立
     *
     * @param path 文件夹路径
     */
    public static void judeDirExists(String path) {
        File file = new File(path);
        if (!file.exists()) {
            System.out.println("dir not exists, create it ...");
            file.mkdir();
        }
    }

}